#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cctype>
#include <ctime>

using namespace std;

#include "EasyBMP.h"
#include "EasyBMP_Geometry.h"
#include "EasyBMP_Font.h"

#include "tinyxml.h"
#include "xml_parser.h"
#include "frame.h"
#include "Matrix.h"
#include "ImprovedMath.h"

#include "globals.h"

#include "misc.h"
#include "ExtendedPlotting.h"

bool LoadSettings( const char* SettingsFileName )
{
 TiXmlDocument doc( SettingsFileName );
 if( !doc.LoadFile() )
 { return false; }

 TiXmlHandle hDoc(&doc);
 TiXmlElement* pElem;
 TiXmlHandle hRoot(0);

 pElem=hDoc.FirstChildElement().Element();
 // should always have a valid root but handle gracefully if it does
 if( !pElem )
 { 
  cout << "Error: Settings file " << SettingsFileName << " is screwed up!" << endl;
  return false;
 }

 TiXmlElement* pCurrentElement = NULL;
 
 // set size information
 
 Width = GetIntegerChildElement( pElem, "width" );
 Height = GetIntegerChildElement( pElem, "height" );
 FontSize = GetIntegerChildElement( pElem, "font_size" );

 // set frame information

 pCurrentElement = FindChildElement( pElem , "frames" );
 if( pCurrentElement )
 {
  TiXmlElement* pPlaceholderElement = pCurrentElement;

  pCurrentElement = FindChildElement( pPlaceholderElement , "top_middle_frame" );
  TopMiddleFrame.SetFromTinyXml( pCurrentElement );
  TopMiddleFrame.SetDataSource( Time );

  pCurrentElement = FindChildElement( pPlaceholderElement , "top_right_frame" );
  TopRightFrame.SetFromTinyXml( pCurrentElement );
  TopRightFrame.SetDataSource( Time );
  
  pCurrentElement = FindChildElement( pPlaceholderElement , "left_frame" );
  LeftFrame.SetFromTinyXml( pCurrentElement );
  LeftFrame.SetDataSource( Time );
  
  pCurrentElement = FindChildElement( pPlaceholderElement , "middle_frame" );
  MiddleFrame.SetFromTinyXml( pCurrentElement );
  MiddleFrame.SetDataSource( Time );

  pCurrentElement = FindChildElement( pPlaceholderElement , "right_frame" );
  RightFrame.SetFromTinyXml( pCurrentElement );
  RightFrame.SetDataSource( Time );

  pCurrentElement = FindChildElement( pPlaceholderElement , "bottom_left_frame" );
  BottomLeftFrame.SetFromTinyXml( pCurrentElement );
  BottomLeftFrame.SetDataSource( Time );
  
  pCurrentElement = FindChildElement( pPlaceholderElement , "bottom_middle_frame" );
  BottomMiddleFrame.SetFromTinyXml( pCurrentElement );
  BottomMiddleFrame.SetDataSource( Time );

  pCurrentElement = FindChildElement( pPlaceholderElement , "bottom_right_frame" );
  BottomRightFrame.SetFromTinyXml( pCurrentElement );
  BottomRightFrame.SetDataSource( Time );
  
  pCurrentElement = pPlaceholderElement;
 }
 
 // set color information

 pCurrentElement = FindChildElement( pElem , "colors" );
 if( pCurrentElement )
 {
  char* szTempText = GetStringChildElement( pCurrentElement,"viable_color");
  ViableColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;
  
  szTempText = GetStringChildElement( pCurrentElement,"hypoxic_color");
  HypoxicColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  
  
  szTempText = GetStringChildElement( pCurrentElement,"necrotic_color");
  NecroticColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  
  
  szTempText = GetStringChildElement( pCurrentElement,"frame_color");
  FrameColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  
  
  szTempText = GetStringChildElement( pCurrentElement,"font_color");
  FontColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  
  
  szTempText = GetStringChildElement( pCurrentElement,"blood_vessel_color");
  BloodVesselColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  

  szTempText = GetStringChildElement( pCurrentElement,"sprout_tip_color");
  SproutTipColor =  OrderedTripleToRGBApixel( szTempText );
  delete szTempText;  
 }
 
 // get filename bases

 pCurrentElement = FindChildElement( pElem , "filename_bases" );
 if( pCurrentElement )
 {
  Phi_base = GetStringChildElement( pCurrentElement,"Phi");
  PhiH_base = GetStringChildElement( pCurrentElement,"PhiH");
  PhiN_base = GetStringChildElement( pCurrentElement,"PhiN");

  Structure_base = GetStringChildElement( pCurrentElement,"Structure");
  
  BloodVesselRadius_base = GetStringChildElement( pCurrentElement,"BloodVesselRadius");
  BloodVesselIndicator_base = GetStringChildElement( pCurrentElement,"BloodVesselIndicator");
  SproutTips_base = GetStringChildElement( pCurrentElement,"SproutTips");
 }
 else
 {
  Phi_base = "Phi";
  PhiH_base = "PhiH";
  PhiN_base = "PhiN";

  Structure_base = "Structure";
  
  BloodVesselRadius_base = "BloodVesselRadius";
  BloodVesselIndicator_base = "BloodVesselIndicator";
  SproutTips_base = "SproutTips";
 }
 
 // get other information 
 
 pCurrentElement = FindChildElement( pElem , "background_image" );
 if( pCurrentElement )
 { BackgroundImage.ReadFromFile( pCurrentElement->GetText() ); }
 
 DiffusionLengthScaleInMicrons = GetIntegerChildElement( pElem, "diffusion_length_scale_in_microns" );
 if( DiffusionLengthScaleInMicrons == 0 )
 { DiffusionLengthScaleInMicrons = 200; }
 
 time_scale_in_hours = GetIntegerChildElement( pElem, "time_scale_in_hours" );
 if( time_scale_in_hours < 1e-4 )
 { time_scale_in_hours = 24.0; }
 
 BlendParameter = GetDoubleChildElement( pElem, "BlendParameter" );
 PlotVessels = false;
 PlotVessels = GetBoolChildElement( pElem, "PlotVessels" );
 
 min_vessel_radius = GetDoubleChildElement( pElem , "min_vessel_radius" );
 max_vessel_radius = GetDoubleChildElement( pElem , "max_vessel_radius" );
 if( fabs( min_vessel_radius - max_vessel_radius ) < 1e-7 )
 {
  min_vessel_radius = 2e-6; 
  max_vessel_radius = 1.4e-5;
 }
 min_vessel_transparency = GetDoubleChildElement( pElem , "min_vessel_transparency" );
 max_vessel_transparency = GetDoubleChildElement( pElem , "max_vessel_transparency" );
 
 created_by_string1 = GetStringChildElement( pElem , "created_by_string1" );
 created_by_string2 = GetStringChildElement( pElem , "created_by_string2" );
 copyright_string1 = GetStringChildElement( pElem , "copyright_string1" );
 copyright_string2 = GetStringChildElement( pElem , "copyright_string2" );
 
 return true;
}

int Scale;
int TotalHeight;
int TotalWidth;

bool SetFrameSize( void )
{
 int TopHeight = Height;
 int MiddleHeight = 0;
 int BottomHeight = 0;
 
 int LeftWidth = Width;
 int MiddleWidth = 0;
 int RightWidth = 0;

 Scale = 2*FontSize;
 
 if( LeftFrame.show_me || MiddleFrame.show_me || RightFrame.show_me )
 { MiddleHeight = Height; }
 if( BottomLeftFrame.show_me || BottomMiddleFrame.show_me || BottomRightFrame.show_me )
 { BottomHeight = Height; }
 
 if( TopMiddleFrame.show_me || MiddleFrame.show_me || BottomMiddleFrame.show_me )
 { MiddleWidth = Width; }
 if( TopRightFrame.show_me || RightFrame.show_me || BottomRightFrame.show_me )
 { RightWidth = Width; }
 
 TotalHeight = 2*Scale + Height + Scale;
 
 TotalWidth = Scale + Width + Scale;
  
         LeftFrame.position_x = Scale;
   BottomLeftFrame.position_x = Scale; 
  
    TopMiddleFrame.position_x = Scale + LeftWidth + Scale;
       MiddleFrame.position_x = Scale + LeftWidth + Scale;
 BottomMiddleFrame.position_x = Scale + LeftWidth + Scale;
 
 bool temp_decision = false;
 
 // position the middle column
 
 temp_decision = false;
 if( LeftFrame.colorbar && LeftFrame.show_me )
 { temp_decision = true; }
 if( BottomLeftFrame.colorbar && BottomLeftFrame.show_me )
 { temp_decision = true; }
 
 if( temp_decision == true )
 {
  TotalWidth += Scale; 
  
     TopMiddleFrame.position_x += Scale; 
        MiddleFrame.position_x += Scale;
  BottomMiddleFrame.position_x += Scale;
 }
 
 // position the right column;
 
    TopRightFrame.position_x =    TopMiddleFrame.position_x;
       RightFrame.position_x =       MiddleFrame.position_x;
 BottomRightFrame.position_x = BottomMiddleFrame.position_x;	
 if( MiddleWidth > 0 )
 {
  TotalWidth += MiddleWidth + Scale;
     TopRightFrame.position_x += MiddleWidth + Scale;
        RightFrame.position_x += MiddleWidth + Scale;
  BottomRightFrame.position_x += MiddleWidth + Scale;
 }
 
 temp_decision = false;
 if( TopMiddleFrame.colorbar && TopMiddleFrame.show_me )
 { temp_decision = true; }
 if( MiddleFrame.colorbar && MiddleFrame.show_me )
 { temp_decision = true; }
 if( BottomMiddleFrame.colorbar && BottomMiddleFrame.show_me )
 { temp_decision = true; }
 
 if( temp_decision == true )
 {
  TotalWidth += Scale; 
  
     TopRightFrame.position_x += Scale; 
        RightFrame.position_x += Scale;
  BottomRightFrame.position_x += Scale;
 }
 
 // finish the width calculation 
 
 if( RightWidth > 0 )
 {
  TotalWidth += RightWidth + Scale;
 }
 
 temp_decision = false;
 if( TopRightFrame.colorbar && TopRightFrame.show_me )
 { temp_decision = true; }
 if( RightFrame.colorbar && RightFrame.show_me )
 { temp_decision = true; }
 if( BottomRightFrame.colorbar && BottomRightFrame.show_me )
 { temp_decision = true; }
 
 if( temp_decision == true )
 { TotalWidth += Scale; }

 // position the top row
 
 TopMiddleFrame.position_y = 2*Scale;
  TopRightFrame.position_y = 2*Scale;
  
 // position the middle row
  
  LeftFrame.position_y = 2*Scale + TopHeight + Scale;  
 MiddleFrame.position_y = TopMiddleFrame.position_y + TopHeight + Scale;  
  RightFrame.position_y = TopRightFrame.position_y + TopHeight + Scale;  
  
 // position the bottom row
 
 BottomLeftFrame.position_y = LeftFrame.position_y;
 BottomMiddleFrame.position_y = MiddleFrame.position_y;
 BottomRightFrame.position_y = RightFrame.position_y;
 
 if( MiddleHeight > 0 )
 {
  TotalHeight += MiddleHeight + Scale;
  
  BottomLeftFrame.position_y += MiddleHeight + Scale;
  BottomMiddleFrame.position_y += MiddleHeight + Scale;
  BottomRightFrame.position_y += MiddleHeight + Scale;
 }
 
 // finish the height calculation
 
 if( BottomHeight > 0 )
 {
  TotalHeight += BottomHeight + Scale; 
 }
 
 if( strlen( created_by_string1 ) )
 { TotalHeight += FontSize; }
 if( strlen( created_by_string2 ) )
 { TotalHeight += FontSize; }
 if( strlen( copyright_string1 ) )
 { TotalHeight += FontSize; }
 if( strlen( copyright_string2 ) )
 { TotalHeight += FontSize; }
 
 return true;
 
 
 TopRightFrame.position_y = 2*Scale;
 BottomRightFrame.position_y = TopRightFrame.position_y + Height + Scale;
 BottomLeftFrame.position_y = BottomRightFrame.position_y;
 BottomLeftFrame.position_x = Scale;
 
 if( BottomRightFrame.show_me || TopRightFrame.show_me )
 {
  TotalWidth += Width + Scale;
  
  bool WidenForColorbar = false;
  if( BottomRightFrame.colorbar && BottomRightFrame.show_me )
  { WidenForColorbar = true; }
  if( TopRightFrame.colorbar && TopRightFrame.show_me ) 
  { WidenForColorbar = true; }

// if( BottomRightFrame.colorbar || TopRightFrame.colorbar )
  if( WidenForColorbar )
  { TotalWidth += Scale; }
 }
 
 BottomMiddleFrame.position_x = BottomRightFrame.position_x;
 BottomMiddleFrame.position_y = BottomRightFrame.position_y;
 TopMiddleFrame.position_x = TopRightFrame.position_x;
 TopMiddleFrame.position_y = TopRightFrame.position_y;
 
 if( BottomMiddleFrame.show_me || TopMiddleFrame.show_me )
 {
  TotalWidth += Width + Scale;
  TopRightFrame.position_x += Width+Scale;
  BottomRightFrame.position_x += Width+Scale;
  
  bool WidenForColorbar = false;
  if( BottomMiddleFrame.colorbar && BottomMiddleFrame.show_me )
  { WidenForColorbar = true; }
  if( TopMiddleFrame.colorbar && TopMiddleFrame.show_me ) 
  { WidenForColorbar = true; }

//  if( BottomMiddleFrame.colorbar || TopMiddleFrame.colorbar )
  if( WidenForColorbar )
  {
   TotalWidth += Scale; 
   TopRightFrame.position_x += Scale;
   BottomRightFrame.position_x += Scale;
  }
 } 

 return true;
}

int main( int argc, char* argv[] )
{
 // process the arguments

 if( argc < 3 )
 {
  cout << "Usage: " << endl
       << "CompositeFrame <time> <output.bmp>" << endl << endl;
  return -1;
 }
 
 SetEasyBMPwarningsOff();

 for( int i=0; i < argc ; i++ )
 {
  if( i == 1 )
  { Time = strtod( argv[i] , NULL ); }
  if( i == 2 )
  {
   OutputFilename = new char [strlen(argv[i])+1];
   strcpy( OutputFilename , argv[i] );
  }
 }
 
 // general setup 
 
 SetGlobalColors();
  
 // load settings
 
 LoadSettings( "movie_settings.xml" );

 // load data
 
 char* PhiFileName = CreateFilename( Phi_base, Time );
 Phi.ReadFromFile( PhiFileName );
 if( Phi.TellRows() == 1 || Phi.TellCols() == 1 )
 {
  cout << "Error: Cannot load file " << PhiFileName << "." << endl
       << "       Game over!" << endl << endl;
  return -1;	
 }
 
 char* PhiHfileName = CreateFilename( PhiH_base, Time );
 PhiH.ReadFromFile( PhiHfileName );
 if( PhiH.TellRows() != Phi.TellRows() || PhiH.TellCols() != Phi.TellCols() )
 {
  cout << "Warning: " << PhiHfileName << " is wrong size and will be ignored!" << endl;
  PhiH = ones( Phi.TellRows() , Phi.TellCols() );
 }
 
 char* PhiNfileName = CreateFilename( PhiN_base, Time );
 PhiN.ReadFromFile( PhiNfileName );
 if( PhiN.TellRows() != Phi.TellRows() || PhiN.TellCols() != Phi.TellCols() )
 {
  cout << "Warning: " << PhiNfileName << " is wrong size and will be ignored!" << endl;
  PhiN = ones( Phi.TellRows() , Phi.TellCols() );
 }
 
 char* StructureFileName = CreateFilename( Structure_base, Time );
 Structure.ReadFromFile( StructureFileName );
 if( Structure.TellRows() != Phi.TellRows() || Structure.TellCols() != Phi.TellCols() )
 {
  cout << "Warning: " << StructureFileName << " is wrong size and will be ignored!" << endl;
  Structure = zeros( Phi.TellRows() , Phi.TellCols() );
 }

 char* XfileName = CreateFilename( "X", Time );
 X.ReadFromFile( XfileName );
 
 if( PlotVessels )
 {
  char* BVIfileName = CreateFilename( BloodVesselIndicator_base, Time );
  BloodVesselIndicator.ReadFromFile( BVIfileName );
  if( BloodVesselIndicator.TellRows() != Phi.TellRows() || BloodVesselIndicator.TellCols() != Phi.TellCols() )
  {
   cout << "Warning: " << BVIfileName << " is wrong size and will be ignored!" << endl;
   BloodVesselIndicator = zeros( Phi.TellRows() , Phi.TellCols() );
  }  
  delete BVIfileName;
  
  char* SproutTipsFileName = CreateFilename( SproutTips_base, Time );
  SproutTips.ReadFromFile( SproutTipsFileName );
  if( SproutTips.TellRows() != Phi.TellRows() || SproutTips.TellCols() != Phi.TellCols() )
  {
   cout << "Warning: " << SproutTipsFileName << " is wrong size and will be ignored!" << endl;
   SproutTips = zeros( Phi.TellRows() , Phi.TellCols() );
  }  
  delete SproutTipsFileName;
  
  char* BVRfileName = CreateFilename( BloodVesselRadius_base, Time );
  BloodVesselRadius.ReadFromFile( BVRfileName );
  if( BloodVesselRadius.TellRows() != Phi.TellRows() || BloodVesselRadius.TellCols() != Phi.TellCols() )
  {
   cout << "Warning: " << BVRfileName << " is wrong size and will be ignored!" << endl;
   BloodVesselRadius = zeros( Phi.TellRows() , Phi.TellCols() );
  }    
  delete BVRfileName;
 }
 
 if( TopMiddleFrame.show_me && TopMiddleFrame.DataSource )
 { TopMiddleData.ReadFromFile( TopMiddleFrame.DataSource); }
 if( TopRightFrame.show_me && TopRightFrame.DataSource )
 { TopRightData.ReadFromFile( TopRightFrame.DataSource); }

 if( LeftFrame.show_me && LeftFrame.DataSource )
 { LeftData.ReadFromFile( LeftFrame.DataSource); }
 if( MiddleFrame.show_me && MiddleFrame.DataSource )
 { MiddleData.ReadFromFile( MiddleFrame.DataSource); }
 if( RightFrame.show_me && RightFrame.DataSource )
 { RightData.ReadFromFile( RightFrame.DataSource); }
 
 if( BottomLeftFrame.show_me && BottomLeftFrame.DataSource )
 { BottomLeftData.ReadFromFile( BottomLeftFrame.DataSource); }
 if( BottomMiddleFrame.show_me && BottomMiddleFrame.DataSource )
 { BottomMiddleData.ReadFromFile( BottomMiddleFrame.DataSource); }
 if( BottomRightFrame.show_me && BottomRightFrame.DataSource )
 { BottomRightData.ReadFromFile( BottomRightFrame.DataSource); }

 // figure out sizes, etc.
 
 SetFrameSize();
 
 CompositeFrame.SetSize( TotalWidth, TotalHeight );
 ClearImage( CompositeFrame , FrameColor );
 
 // do the top left image 
 if( BackgroundImage.TellWidth() == 1 || BackgroundImage.TellHeight() == 1 )
 {
  BackgroundImage.SetSize( Phi.TellRows() ,Phi.TellCols() );
  ClearImage(BackgroundImage , White );
 }

 BMP Frame;
 Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
 for( int j=0 ; j < Frame.TellHeight() ; j++ )
 {
  int J = Phi.TellCols() - j - 1;
  bool done = false;
  for( int i=0 ; i < Frame.TellWidth() ; i++ )
  {
   int I = i;
   if( *PhiN(i,j) <= _EPS )
   {
    *Frame(I,J) = NecroticColor;
	done = true;
   }
   else
   {
    if( *PhiH(i,j) <= _EPS && *Phi(i,j) <= _EPS )
	{
	 *Frame(I,J) = HypoxicColor;
	 done = true;
	}
	else
	{
	 if( *Phi(i,j) <= _EPS )
	 { 
	  *Frame(I,J) = ViableColor;
	  done = true; 
	 }
	 else
	 { *Frame(I,J) = White; }
	}
   }
   
   if( PlotVessels )
   {
    if( *BloodVesselIndicator(i,j) > 0.05 )
	{ 
	 Frame(I,J)->Red = Frame(I,J)->Red / 2; //  + (255 - Frame(I,J)->Red)/2;
	 Frame(I,J)->Green = Frame(I,J)->Green / 4; //  + (255 - Frame(I,J)->Green)/2;
	 Frame(I,J)->Blue = Frame(I,J)->Blue / 4; //  + (255 - Frame(I,J)->Blue)/2;
	 
/*
     // overwrote this with the current, semi-transparent method on 8-21-2007
 	 double temp_interp = ( *BloodVesselRadius(i,j) - min_vessel_radius )
	                     /( max_vessel_radius - min_vessel_radius );
     if( temp_interp > 1.0 ){ temp_interp = 1.0; }
     if( temp_interp < 0.0 ){ temp_interp = 0.0; }	 
     double temp_transparency = (1.0-temp_interp)*(min_vessel_transparency)
                              + temp_interp*max_vessel_transparency;
     Frame(I,J)->Red = (ebmpBYTE) (     (temp_transparency)*Frame(I,J)->Red 
                                   +(1.0-temp_transparency)*BloodVesselColor.Red );	 
     Frame(I,J)->Green = (ebmpBYTE) (   (temp_transparency)*Frame(I,J)->Green 
                                   +(1.0-temp_transparency)*BloodVesselColor.Green );	 
     Frame(I,J)->Blue = (ebmpBYTE) (    (temp_transparency)*Frame(I,J)->Blue 
                                   +(1.0-temp_transparency)*BloodVesselColor.Blue );	
*/								   
    }								   
	if( *SproutTips(i,j) > 0.05 )
	{
 	 *Frame(I,J) = SproutTipColor; 
	}
   }
   
  }
 }
 
 OverlayImage_MultiplicationBlending( BackgroundImage, Frame, BlendParameter );
 Rescale( BackgroundImage , 'w', Width );
 DrawFrameBorder( BackgroundImage );
 RangedPixelToPixelCopy( BackgroundImage,0,Width-1,0,Height-1,CompositeFrame,
                         Scale,2*Scale); 
 PrintString( CompositeFrame, "Tissue Overlay" , 
              Scale + FontSize/2, TopRightFrame.position_y - FontSize - FontSize/2, 
			  FontSize, FontColor );

 // determine the height of the length bar in pixels
 
 double dx = *X(1) - *X(0);

 int OneCentimeterInPixels = ImprovedRound( (10000.0*Height) 
                             / ( dx*DiffusionLengthScaleInMicrons*(Phi.TellCols()-1.0) ) );
 int OneMillimeterInPixels = ImprovedRound( (1000.0*Height) 
                             / ( dx*DiffusionLengthScaleInMicrons*(Phi.TellCols()-1.0) ) );
 char LengthBarLabel [5];
 int LengthBarHeight;
 if( OneCentimeterInPixels > (8*Height)/10 )
 {
  LengthBarHeight = 0;
  int Bars = 0;
  while( Bars*OneMillimeterInPixels < (4*Height)/10 )
  {
   Bars++;
  }   
  Bars--;
  
  if( Bars == 0 )
  { Bars = 1; }
  
  LengthBarHeight = Bars*OneMillimeterInPixels;
  sprintf( LengthBarLabel , "%u mm", Bars );
 }
 else
 {
  strcpy( LengthBarLabel , "2 mm" );
  LengthBarHeight = OneCentimeterInPixels;
 }
 
 PlotLengthBar( CompositeFrame, Scale+Width+Scale/8, 2*Scale, (3*Scale)/4 , LengthBarHeight, LengthBarLabel );

 // create the tumor overlay, as well as the vessel overlay
 
 BMP TumorOutlineOverlay;
 TumorOutlineOverlay.SetSize( Phi.TellRows() , Phi.TellCols() );
 ClearImage( TumorOutlineOverlay, White );
 
 BMP BloodVesselOverlay;
 BloodVesselOverlay.SetSize( Phi.TellRows() , Phi.TellCols() );
 ClearImage( BloodVesselOverlay, White );
 
 double OutlineThickness = 0.0075 * dx * Phi.TellCols(); // 0.15;
 
 for( int j=1; j < TumorOutlineOverlay.TellHeight()-1 ; j++ )
 {
  int J = Phi.TellCols() - j - 1;
  for( int i=1; i < TumorOutlineOverlay.TellWidth()-1 ; i++ )
  {
   int I = i;
   if( fabs( *Phi(I,J) ) <= OutlineThickness  )
   { *TumorOutlineOverlay(i,j) = Black; }
   
   if( PlotVessels )
   {
    if( *BloodVesselIndicator(I,J) > 0.05 )
    { *BloodVesselOverlay(i,j) = BloodVesselColor; }
    if( *SproutTips(I,J) > 0.05 )
    { *BloodVesselOverlay(i,j) = SproutTipColor; }
   }
	
   // use a simple gradient edge detect to find the bone
	
   double sx = 0;
   double sy = 0;
//   if( i > 0 && i < Xnodes-1 )
   { sx = *Structure(I+1,J) - *Structure(I-1,J); }
//   if( j > 0 && j < Ynodes-1 )
   { sy = *Structure(I,J+1) - *Structure(I,J-1); }
   double grad_s = square(sx) + square(sy) ;

   if( grad_s > 0.005 )
   { *TumorOutlineOverlay(i,j) = Black; }
  }
 
 }
 
 // do the top middle image, if indicated
 
 frame* FramePointer = &TopMiddleFrame;
 Matrix* MatrixPointer = &TopMiddleData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }

 // do the top right image, if indicated 

 FramePointer = &TopRightFrame;
 MatrixPointer = &TopRightData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   } 
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
 
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
//  PrintString( CompositeFrame, TopMiddleFrame.DataName , 
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the left image, if indicated;

 FramePointer = &LeftFrame;
 MatrixPointer = &LeftData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
//  PrintString( CompositeFrame, TopMiddleFrame.DataName , 
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the middle image, if indicated;

 FramePointer = &MiddleFrame;
 MatrixPointer = &MiddleData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
   
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the right image, if indicated;

 FramePointer = &RightFrame;
 MatrixPointer = &RightData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the bottom left image, if indicated;

 FramePointer = &BottomLeftFrame;
 MatrixPointer = &BottomLeftData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{	
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the bottom middle image, if indicated;

 FramePointer = &BottomMiddleFrame;
 MatrixPointer = &BottomMiddleData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }
 
 // do the bottom right image, if indicated;

 FramePointer = &BottomRightFrame;
 MatrixPointer = &BottomRightData;
 
 if( FramePointer->show_me && MatrixPointer->TellRows() > 1 )
 {
  Frame.SetSize( Phi.TellRows() , Phi.TellCols() );
  
  if( FramePointer->Use_MinMax_scale )
  {
   if( FramePointer->special_blood_vessel_plot )
   {
    FindVesselMinAndMax( *MatrixPointer, BloodVesselIndicator, &(FramePointer->min_scale), &(FramePointer->max_scale) );
   } 
   else
   {
    FindMinAndMax( *MatrixPointer, &(FramePointer->min_scale) , &(FramePointer->max_scale) );
   }
  }
  
  if( FramePointer->reverse_colors )
  {
   double temp_scale = FramePointer->min_scale;
   FramePointer->min_scale = FramePointer->max_scale;
   FramePointer->max_scale = temp_scale;  
  }
  
  for( int j=0 ; j < Frame.TellHeight() ; j++ )
  {
   int J = Phi.TellCols() - j - 1;
   for( int i=0 ; i < Frame.TellWidth() ; i++ )
   {
    int I = i;
	if( FramePointer->special_blood_vessel_plot )
	{
     if( *BloodVesselIndicator(i,j) > 0.05 )
	 {
      *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) , FramePointer->max_scale ); 
	 }
	 else
	 {
	  Frame(I,J)->Red = 255 - (FramePointer->tumor_outline_color).Red;
	  Frame(I,J)->Green = 255 - (FramePointer->tumor_outline_color).Green;
	  Frame(I,J)->Blue = 255 - (FramePointer->tumor_outline_color).Blue;
	 }
	}
	else
	{
     *Frame(I,J) = (FramePointer->color_function)( FramePointer->min_scale, *(*MatrixPointer)(i,j) ,FramePointer->max_scale);
	}
	
    if( fabs( *Phi(i,j) ) <= OutlineThickness && FramePointer->outline_tumor )
    { *Frame(I,J) = FramePointer->tumor_outline_color; }	
	
	if( *BloodVesselIndicator(i,j) > 0.05 && FramePointer->plot_vessels )
    { *Frame(I,J) = FramePointer->vessel_color; }
   }
  }
  
  Rescale( Frame , 'w', Width );
  DrawFrameBorder( Frame );
  
  if( FramePointer->colorbar )
  {
   FramePointer->draw_colorbar_function( CompositeFrame , FramePointer->min_scale, FramePointer->max_scale,
   FramePointer->position_x + Width + Scale/4 , FramePointer->position_y , 
   (3*Scale)/4, Height );  
  }
  
  PrintString( CompositeFrame, FramePointer->Label , 
               FramePointer->position_x + FontSize, FramePointer->position_y - FontSize - FontSize/2, 
	 		   FontSize, FontColor );
  
  RangedPixelToPixelCopy( Frame,0,Width-1,0,Height-1,CompositeFrame,
                          FramePointer->position_x,FramePointer->position_y);
 }

 // plot the simulation time
 
 char TimeString [1024];
 sprintf( TimeString , "Time = %3.2f days", Time*time_scale_in_hours/24.0 );
 
 PrintString( CompositeFrame, TimeString , Scale, Scale/4 , (3*Scale)/4, FontColor );
 
 // write copyright, etc.
 
 int TempPositionX = BottomLeftFrame.position_x;
 int TempPositionY = BottomLeftFrame.position_y;
 int OriginalPositionX = TempPositionX;
 
 if( BottomLeftFrame.show_me || BottomMiddleFrame.show_me || BottomRightFrame.show_me )
 { TempPositionY += Height; }
 else
 { TempPositionY -= Scale; }
 TempPositionY += ImprovedRound( 0.5*Scale + 0.05*FontSize ); 
 
 if( strlen( copyright_string1 ) )
 {
  char TempString [18];
  
  // get the current year
  
  time_t rawtime;
  struct tm* timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  int year = 1900 + timeinfo->tm_year;
  
  // create the copyright string
  
  sprintf( TempString , "Copyright c %u ", year );
  TempString[10] = COPYRIGHT_SYMBOL;
  TempPositionX = PrintString(CompositeFrame, TempString, 
                              TempPositionX, TempPositionY, (3*FontSize)/4 ,FontColor );
  PrintString(CompositeFrame, copyright_string1, 
                              TempPositionX, TempPositionY, (3*FontSize)/4 ,FontColor );
  TempPositionY += FontSize;
  
  TempPositionX = OriginalPositionX;
  TempPositionX = PrintString(CompositeFrame, "     ", 
                              TempPositionX, TempPositionY, (3*FontSize)/4 ,FontColor );
  
  if( strlen( copyright_string2 ) )
  {
   PrintString(CompositeFrame, copyright_string2, 
                              TempPositionX, TempPositionY, (3*FontSize)/4 ,FontColor );
   TempPositionY += FontSize;
   TempPositionX = OriginalPositionX;
  }
 }
 
 int TempFontSize = (6*FontSize)/10;
 if( TempFontSize % 2 == 1 )
 { TempFontSize++; }
 
 if( strlen( created_by_string1 ) )
 {
 
  TempPositionY += FontSize/2;
  PrintString(CompositeFrame, created_by_string1, 
                              TempPositionX, TempPositionY, TempFontSize ,FontColor );
  TempPositionY += FontSize;
 }
 
 if( strlen( created_by_string2 ) )
 {
  TempPositionX = PrintString(CompositeFrame, "     ", 
                              TempPositionX, TempPositionY, (3*FontSize)/4 ,FontColor );
  PrintString(CompositeFrame, created_by_string2, 
                              TempPositionX, TempPositionY, TempFontSize, FontColor );
 }

 // write the file
 
 CompositeFrame.WriteToFile( OutputFilename ); 
 
 return 0;
}
